#include <stdio.h>
#include "STLOG.h"
#include "STL_Serial.h"
#include "STL_Text.h"
#include <unistd.h>
#include <memory>

using namespace STL;

// 协议格式
// 开始字符	设备名称H	设备名称L	目的地址	源地址	命令	数据长度	数据	校验和	结束字符
// 0x7E	   1Byet	   1Byte	  1Byte	     1Byte	 1Byte	1Byte	   nBytes	1Byte	0x7E

// 说明
// 1. 数据长度为“数据”字段的数据长度,不包含校验和字节和结束字节;
// 2. 校验和为目的地址、源地址、命令、数据长度、数据之和;
// 3. 地址范围0 - 15,255为广播地址,所有设备都执行相应的命令;
// 4. 源地址保留,为255;
// 5. 如果与“开始字符”和“结束字符”重合,0x7E ==> 0x7D 0x02,0x7D ==> 0x7D 0x01;
// 6. 设备类型,A6216为0x62(H)+0x16(L),A7208为0x72(H)+0x08(L);
// 7. 升级设备发送0x60指令后,收到单片机回复指令后直接发送update.bin文件即可,升级完成单片机回复信息;

#define START_CODE 0x7e
#define END_CODE 0x7e

#define DEVICE_H 0x72
#define DEVICE_L 0x08

#define DEVICE_ADDR 0x00
#define SRCOUT_ADDR 0xfe

// A7208协议:
// 命令	描述	参数	备注
// 0x53	查询设备和电源状态	         无参数
// 0x10	控制全部电源	            1Byte
// 0x11	控制单独电源	            2Bytes
// 0x20	设置电源延迟关闭时间	     1Byte
// 0x21	设置电源全部打开间隔时间	 1Byte
// 0x60	设备升级判断	            8Bytes
// 0x61	发送升级数据	            nBytes

#define CMD_QUERY 0x53        // 查询设备和电源状态 0xff 打开全部 0x00 关闭全部
#define CMD_ALL_CTR 0x10      // 控制全部电源
#define CMD_ALONE_CTR 0x11    // 控制单独电源
#define CMD_SET_DELAY 0x20    // 设置电源延迟关闭时间
#define CMD_SET_INTERVAL 0x21 // 设置电源全部打开间隔时间
#define CMD_UPGRADE_TEST 0x60 // 设备升级判断
#define CMD_UPGRADE_DATA 0x61 // 发送升级数据

#define CMD_OPEN_FORCE 0x13  // 彦涛神秘命令 强制开启
#define CMD_CLOSE_FORCE 0x14 // 彦涛神秘指令 强制关闭

std::string addTail(std::string data)
{
    uint32_t check = 0;
    for (int i = 1; i < data.size(); i++)
    {
        check += data.at(i);
    }
    data.append(1, check & 0xff);

    data.append(1, END_CODE);
    return data;
}

std::string genOpenForce()
{
    std::string data = "";
    data.append(1, START_CODE);
    data.append(1, DEVICE_H);
    data.append(1, DEVICE_L);
    data.append(1, DEVICE_ADDR);
    data.append(1, SRCOUT_ADDR);

    data.append(1, CMD_OPEN_FORCE);

    std::string args = "";
    args.append(1, 0xff);

    data.append(1, args.size() & 0xff);
    data.append(args);

    return addTail(data);
}

std::string genCloseForce()
{
    std::string data = "";
    data.append(1, START_CODE);
    data.append(1, DEVICE_H);
    data.append(1, DEVICE_L);
    data.append(1, DEVICE_ADDR);
    data.append(1, SRCOUT_ADDR);

    data.append(1, CMD_CLOSE_FORCE);

    std::string args = "";
    args.append(1, 0x00);

    data.append(1, args.size() & 0xff);
    data.append(args);

    return addTail(data);
}

std::string genAloneCtr(int index, bool value)
{
    if (index < 1 || index > 8)
    {
        return "";
    }

    std::string data = "";
    data.append(1, START_CODE);
    data.append(1, DEVICE_H);
    data.append(1, DEVICE_L);
    data.append(1, DEVICE_ADDR);
    data.append(1, SRCOUT_ADDR);

    data.append(1, CMD_ALONE_CTR);

    std::string args = "";
    args.append(1, index & 0xff);
    if (value)
    {
        args.append(1, 0x01);
    }
    else
    {
        args.append(1, 0x00);
    }
    data.append(1, args.size() & 0xff);
    data.append(args);
    return addTail(data);
}

int main(int argc, char *argv[])
{
    std::string serial_path = "/dev/ttyS3";

    // std::string serial_path = std::string(argv[1]);

    STL_Serial::SerialConfig config(serial_path, 9600);
    STL_Serial *serial = new STL_Serial(config);
    serial->setDataCallback([=](std::shared_ptr<std::string> sp_data)
                            { stlog_debug("recv:%s", STL_Text::StringToHexString(*sp_data).c_str()); });
    serial->openSerial();

    int index = 1;
    bool value = true;
    while (1)
    {
        // if (index > 8)
        // {
        //     index = 1;
        //     value = !value;
        // }

        // if (value)
        // {
        //     stlog_debug("index[%d],value[%s]", index, value ? "true" : "false");
        //     auto data = genAloneCtr(index, value);
        //     stlog_debug("send:%s", STL_Text::StringToHexString(data).c_str());
        //     serial->sendData(data);
        // }
        if (value)
        {
            stlog_debug("genOpenForce");
            auto data = genOpenForce();
            stlog_debug("send:%s", STL_Text::StringToHexString(data).c_str());
            serial->sendData(data);
            index = 0;
            value = false;
        }
        else
        {
            stlog_debug("genCloseForce");
            auto data = genCloseForce();
            stlog_debug("send:%s", STL_Text::StringToHexString(data).c_str());
            serial->sendData(data);
            index = 0;
            value = true;
        }
        sleep(10);
        index++;
    }
    return 0;
}